﻿@page "/reports"
@page "/reports/{RunGuid}"
@using Microsoft.CodeAnalysis.Sarif;
@using System.Text; 
@using MoreLinq; 
@inject Blazored.LocalStorage.ILocalStorageService localStorage;

<h1>Report Viewer</h1>

<div>Select Run</div>
<Dropdown TItem="string" OnSelected="@OnSelected">
    <ChildContent>
        <div class="dropdown-divider"></div>
        @foreach (var runId in RunList)
        {
            <DropdownListItem Item="@runId">@runId</DropdownListItem>
        }
    </ChildContent>
</Dropdown>

<br />

<button @onclick="GetResults">Get Results</button>
<button @onclick="ClearLocalStorage">Clear Storage</button>

<br />

<pre style="width:800px; height:70px; border-style:solid; border-color:black; padding:10px">@Status</pre>
<div style="width:800px; height:1200px; border-style:solid; border-color:black; padding:10px">
    <div style="font-weight: bold;">Summary for Run ID: @runId</div>
    <div class="row">
        <div class="column" style="width:50%; padding-left:30px">
            <div style="font-weight: bold;"># Files Scanned: </div> <div>@FileCount</div>
            <div style="font-weight: bold;"># Rules Triggered: </div> <div>@RuleList.Count</div>
        </div>
        <div class="column" style="width:50%;">
            <div style="font-weight: bold;"># Findings: </div> <div>@Occurrences</div>
            <div style="font-weight: bold;"># Tags Identified: </div> <div>@TagList.Count</div>
        </div>
    </div>

    <div style="font-weight: bold;">Tag List</div>
    <ul>
        @foreach (var tag in TagList)
        {
            <li>@tag</li>
        }
    </ul>

    <div style="font-weight: bold;">Files with detected issues</div>
    @foreach (var finding in Findings)
    {
        <div>
            <i>@finding.Value.Item4:</i> <span>@finding.Key</span> <a href="viewcode/@finding.Value.Item1">@finding.Value.Item2 @finding.Value.Item3 found.</a>
        </div>
    }
</div>

@code {
    [Parameter]
    public string RunGuid { get; set; } = string.Empty;
    private static string nl = Environment.NewLine;
    private string runId = "";
    private string SelectedRun { get; set; } = "";
    private int Occurrences = 0;
    private int FileCount = 0;
    private HashSet<string> TagList = new HashSet<string>();
    private HashSet<string> RuleList = new HashSet<string>();
    private HashSet<string> FindingLocations = new HashSet<string>();
    public string Status { get; set; } = "Idle";
    public IList<string> RunList = new List<string> { };
    public IDictionary<string, (string,int,string, FailureLevel)> Findings = new Dictionary<string, (string, int,string, FailureLevel)>();
    private Results results;

    protected override async Task OnAfterRenderAsync(bool firstRender)
    {
        if (firstRender)
        {
            await ReadStorage();
            if (!string.IsNullOrEmpty(RunGuid))
            {
                SelectedRun = results.RunIdMap[RunGuid];
            }
            else
            {
                SelectedRun = results.RunIdMap.Values.Select(runId => (DateTime.Parse(runId), runId)).MaxBy(x => x.Item1).First().runId;
            }
            await GetResults();
        }
    }

    public async void ClearLocalStorage()
    {
        SelectedRun = "";
        Status = "Idle";
        RunList.Clear();
        Findings.Clear();
        await localStorage.ClearAsync();
        StateHasChanged();
    }

    private void OnSelected(string selection)
    {
        SelectedRun = selection;
        GetResults();
    }

    public async Task ReadStorage()
    {
        try
        {
            Status = $"Reading storage...{nl}";
            this.StateHasChanged();
            var res = await localStorage.GetItemAsync<Results>("DevSkimResults");
            if (res == null)
                res = new Results();
            results = res;
            RunList = res.RunIdMap.Values.ToList();

            Status = $"Found {RunList.Count} runs.{nl}";
            this.StateHasChanged();
        }
        catch (Exception e)
        {
            var message = e.Message;
            var stackTrace = e.StackTrace;
            var type = e.GetType();
            var name = type.Name;
            Console.WriteLine(e.Message);
        }
    }

    public async Task GetResults()
    {
        try
        {
            Occurrences = 0;
            FileCount = 0;
            TagList.Clear();
            RuleList.Clear();
            FindingLocations.Clear();
            Findings.Clear();

            runId = SelectedRun;

            if (String.IsNullOrWhiteSpace(runId))
            {
                Status = $"No run selected.{nl}";
                this.StateHasChanged();
                return;
            }

            Status = $"Reading results of Run ID: {runId}{nl}";
            this.StateHasChanged();

            var res = await localStorage.GetItemAsync<Results>("DevSkimResults");

            // Load sarif results
            var location = res.ResultLocations[runId];
            var sarif = await localStorage.GetItemAsync<string>(location);
            using var stream = new System.IO.MemoryStream(Encoding.UTF8.GetBytes(sarif));
            var loadedSarif = SarifLog.Load(stream);
            var sarifResults = loadedSarif.Runs[0].Results;


            // Read results for summary
            if (sarifResults != null)
            {
                foreach (var result in sarifResults)
                {
                    foreach (var tag in result.Tags)
                    {
                        TagList.Add(tag);
                    }
                    Occurrences += result.OccurrenceCount;
                    RuleList.Add(result.RuleId);
                }
            }
            FileCount = res.FileLocations.Count;

            // Enumerate findings
            foreach (var loc in res.FileLocations[runId])
            {
                var rulesWhichApply = loadedSarif.Runs[0].Results.Where(x => x.Locations.Any(y => y.PhysicalLocation.Address.FullyQualifiedName == loc.Key));
                var severities = loadedSarif.Runs[0].Tool.Driver.Rules.Where(y => rulesWhichApply.Any(x => x.RuleId == y.Id));
                var severity = FailureLevel.Note;
                if (severities.Any())
                    severity = severities.Max(x => x.DefaultConfiguration.Level);
                if (rulesWhichApply.Count() is int count && count > 0)
                {
                    Findings.Add(loc.Key, (loc.Value, count, count == 1 ? "finding" : "findings", severity));
                }
            }
            Status += $"Finished reading results of Run ID: {SelectedRun}{nl}";
            this.StateHasChanged();
        }
        catch (Exception e)
        {
            var message = e.Message;
            var stackTrace = e.StackTrace;
            var type = e.GetType();
            var name = type.Name;
            Console.WriteLine(e.Message);
        }
    }
}